package anveshitha.indexers;

import static anveshitha.config.AnveshithaConstants.FILE_SEPARATOR;
import static anveshitha.config.AnveshithaConstants.LUCENE_INDEX_DIR;
import static anveshitha.config.AnveshithaConstants.indexFileCounter;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.TimerTask;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.TrueFileFilter;
import org.apache.log4j.Logger;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.Term;

import anveshitha.config.ConfigUtils;
import anveshitha.config.AnveshithaConstants.FieldEnum;
import anveshitha.file.filters.NewFileAndSuffixFileFilter;


/**
 * This interface is for all source code indexers
 * @author varun
 * 
 */
public abstract class SourceCodeIndexer extends TimerTask{

	private static Logger log = Logger.getLogger(SourceCodeIndexer.class);
	
	File fIndexDir;
	String[] srcDirs;
	boolean recursive;
	Analyzer codeAnalyzer;
	String fileExtn;

	public SourceCodeIndexer(String fileExtn, String[] srcDirs, boolean recursive,
			Analyzer codeAnalyzer) {
		super();
		this.fileExtn = fileExtn;
		this.fIndexDir = new File(LUCENE_INDEX_DIR + fileExtn + FILE_SEPARATOR);
		this.srcDirs = srcDirs;
		this.recursive = recursive;
		this.codeAnalyzer = codeAnalyzer;		
	}

	@SuppressWarnings("unchecked")
	public void index() throws FileNotFoundException {

		indexFileCounter = 0;		
		Collection<File> alFileList = getFilesToBeIndexed();
	
		IndexWriter indexWriter = null;
		try { 
			indexWriter = new IndexWriter(fIndexDir, codeAnalyzer);
			
			//delete docs to be updated
			//XXX: Assumes absolute file paths uniquely identify files
			//XXX: also deleted files still stay in the index
			deleteFiles(alFileList, indexWriter);
			
			//index the new docs
			indexFiles(alFileList, indexWriter);
			
			//update indexed time in the properties file
			ConfigUtils.writeLastIndexedTime(fileExtn, System.currentTimeMillis());
			
		} catch (IOException ioe) {
			log.error("IOException : " + ioe.getMessage(), ioe);
		} catch (Exception e) {
			log.error("Exception : " + e.getMessage(), e);
		} finally {
			try {
				indexWriter.close();
			} catch (Exception e) {
				log.error("Error closing indexwriter", e);
			}
		}
	}

	private void deleteFiles(Collection<File> alFileList, IndexWriter indexWriter) throws Exception {
		File tempFile = null;
		Term[] termsToBeDeleted = new Term[alFileList.size()];
		int count = 0;
		
		//create a list of terms based on file names
		for (Iterator<File> iter = alFileList.iterator(); iter.hasNext();count++) {
				tempFile = iter.next();
				termsToBeDeleted[count] = new Term(FieldEnum.FILEPATH.name(), tempFile.getAbsolutePath());
		}
		
		//delete them from index
		indexWriter.deleteDocuments(termsToBeDeleted);
	}

	private void indexFiles(Collection<File> alFileList, IndexWriter indexWriter) {
		File tempFile = null;
		for (Iterator<File> iter = alFileList.iterator(); iter.hasNext();) {
			try {
				tempFile = iter.next();
				indexFile(indexWriter, tempFile);
				indexFileCounter++;
			} catch (Exception e) {
				log.error("Error Creating Index on " + tempFile.getAbsolutePath(), e);
			}
		}
	}

	@SuppressWarnings("unchecked")
	private Collection<File> getFilesToBeIndexed() {
		
		Collection<File> alFileList = new ArrayList<File>();
		long lastIndexedTime = Long.parseLong(ConfigUtils.getLastIndexedTime(fileExtn));
		for(String srcDir: srcDirs){
		
			alFileList.addAll(FileUtils.listFiles(new File(srcDir),
				new NewFileAndSuffixFileFilter(lastIndexedTime, fileExtn),
				TrueFileFilter.INSTANCE));
		}
		return alFileList;
	}

	public abstract void indexFile(IndexWriter indexWriter, File file) throws Exception;
}
